package org.jeecg.modules.flow.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.apache.commons.io.FileUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.flow.query.SaveModelQuery;
import org.jeecg.modules.flow.vo.DeploymentVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

@RestController
@Api(tags = "模型管理")
@RequestMapping("/flow/model")
@Slf4j
public class ModelController {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 保存模型并部署模型
     *
     * @param query
     * @return
     */
    @ApiOperation(value = "保存模型、部署模型")
    @PostMapping("/save")
    public Result<DeploymentVo> save(@RequestBody @Validated SaveModelQuery query) {
        // 新建一个模型
        Model modelData = repositoryService.newModel();
        // 模型的名称
        modelData.setName(query.getName());
        // 模型的key
        modelData.setKey(query.getKey());
        // 模型的分类
        modelData.setCategory(query.getCategory());
        // 封装成json格式
        ObjectNode modelObjectNode = objectMapper.createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, query.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, query.getDescription());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        // 设置模型的MetaInfo---就是act_re_model表中的META_INFO字段
        modelData.setMetaInfo(modelObjectNode.toString());
        byte[] xmlBytes = query.getBpmn_xml().getBytes(StandardCharsets.UTF_8);

        DeploymentVo deploymentVo = new DeploymentVo();
        deploymentVo.setModelId(modelData.getId());
        try {
            // 保存模型---只保存模型的信息，往act_re_model 表中添加一条模型信息
            repositoryService.saveModel(modelData);
            // 保存模型数据---往act_ge_bytearray表中添加模型的数据
            repositoryService.addModelEditorSource(modelData.getId(), xmlBytes);
            // 保存模型的svg格式数据
            // repositoryService.addModelEditorSourceExtra(modelData.getId(), svg.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("模型保存失败{}", e.getMessage());
            return Result.error("模型保存失败", deploymentVo);
        }

        Deployment deploy = null;
        try {
            deploy = repositoryService.createDeployment()
                    // 部署名称
                    .name(modelData.getName())
                    // 部署流程的key
                    .key(modelData.getKey())
                    // 部署的流程分类
                    .category(modelData.getCategory())
                    // 在部署时会检测已部署的相同文件的最后一条记录，如果内容相同，则不会部署
                    .enableDuplicateFiltering()
                    // 资源名称 后缀要带.bpmn20.xml
                    .addString(modelData.getName().concat(".bpmn20.xml"), query.getBpmn_xml())
                    .deploy();
        } catch (Exception e) {
            log.error("根据modelId:{}部署流程,异常:{}", modelData.getId(), e);
            return Result.error("模型部署失败", deploymentVo);
        }
        if (deploy != null) {
            deploymentVo.setDeploymentId(deploy.getId());
        }

        return Result.OK("模型保存成功", deploymentVo);
    }

    @PostMapping("/export")
    @ApiOperation(value = "根据modelId导出流程图xml数据", notes = "根据modelId导出流程图xml数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "modelId", value = "设计的流程图模型ID", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "fileName", value = "文件名称", dataType = "String", paramType = "query")

    })
    public Result<?> exportModelXml(String modelId, String fileName) {
        byte[] modelEditorSource = repositoryService.getModelEditorSource(modelId);
        if (null == modelEditorSource) {
            return Result.error("model不存在,导出失败", null);
        }
        ByteArrayInputStream in = new ByteArrayInputStream(modelEditorSource);
        try {
            FileUtils.copyInputStreamToFile(in, new File("./" + fileName + ".bpmn20.xml"));
        } catch (IOException e) {
            log.error("导出失败:{}，msg:{}", modelId, e);
            return Result.error("导出失败", null);
        }
        return Result.OK("导出成功", null);
    }

    @GetMapping("/delete")
    @ApiOperation(value = "根据模型id删除模型")
    public Result<?> deleteModelById(String modelId) {
        try {
            repositoryService.deleteModel(modelId);
        } catch (Exception e) {
            return Result.error("删除失败");
        }
        return Result.OK("删除成功");

    }


    public static byte[] addTaskListener(byte[] xmlBytes) throws XMLStreamException {
        InputStream bpmnStream = new ByteArrayInputStream(xmlBytes);
        InputStreamReader in = new InputStreamReader(bpmnStream, StandardCharsets.UTF_8);
        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        flowElements.forEach(flowElement -> {
            if (flowElement instanceof UserTask) {
                ExtensionElement extensionElement = generateTaskListenerExtensionElement();
                flowElement.addExtensionElement(extensionElement);
            }
        });

        BpmnModel newBpmnModel = new BpmnModel();

        newBpmnModel.addProcess(mainProcess);

        byte[] bytes = new BpmnXMLConverter().convertToXML(newBpmnModel);

        return bytes;
    }

    /**
     * 创建任务监听器扩展属性
     *
     * @return
     */
    public static ExtensionElement generateTaskListenerExtensionElement() {
        ExtensionElement ee = new ExtensionElement();
        ee.setName("activiti:taskListener");
        ee.setNamespacePrefix("activiti");

        Map<String, List<ExtensionAttribute>> attributes = new HashMap<>();

        List<ExtensionAttribute> attributeList = new ArrayList<>();

        ExtensionAttribute attribute = new ExtensionAttribute();
        attribute.setName("class");
        attribute.setValue("org.jeecg.modules.flow.listener.GlobalPermissionTaskListener");
        attributeList.add(attribute);

        attributes.put("hello", attributeList);

        ee.setAttributes(attributes);
        return ee;
    }

    @PostMapping(path = "/deploy")
    @ApiOperation(value = "根据modelId部署流程")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "modelId", value = "设计的流程图模型ID", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "category", value = "部署流程的分类", dataType = "String", paramType = "query")
    })
    public Result<?> deployByXmlData(@RequestParam String modelId, String category) {
        Deployment deployment = null;
        try {
            Model modelInfo = repositoryService.getModel(modelId);

            byte[] sourceBytes = repositoryService.getModelEditorSource(modelId);

            String xmlStr = new String(sourceBytes, StandardCharsets.UTF_8);

            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                    // 部署名称
                    .name(modelInfo.getName())
                    // 部署流程的key
                    .key(modelInfo.getKey())
                    // 部署的流程分类
                    .category(category)
                    // 在部署时会检测已部署的相同文件的最后一条记录，如果内容相同，则不会部署
                    .enableDuplicateFiltering()
                    // 资源名称 后缀要带.bpmn20.xml
                    .addString(modelInfo.getName().concat(".bpmn20.xml"), xmlStr);
            deployment = deploymentBuilder.deploy();
        } catch (Exception e) {
            log.error("根据modelId:{}部署流程,异常:{}", modelId, e);
            return Result.error("部署失败", e.getMessage());
        }
        if (null != deployment) {
            return Result.OK("部署成功", deployment.getId());
        }

        return Result.error("部署异常", null);
    }
}
